__author__ = 'Brandon Corfman'
import random
from grid import Grid
from problem import FindLadderProblem
from search import dfs
from path import Goal, Ladder, Path

class Board:
    def __init__(self):
        self.clear()

    def __repr__(self):
        """
        Prints the board representation as unicode.
        """
        u = ' '
        for i in range(self.pathlen):
            u += str(i)[-1]
        u += '\n'
        for i in range(self.pathlen):
            u += str(i)[-1]
            for j in range(self.pathlen):
                item = self.path[i][j]
                if item is None:
                    u += ' '
                elif isinstance(item, Path):
                    u += repr(item) # repr prints only single chars for each Path
                else:
                    u += '?'
            u += '\n'
        return u

    def clear(self):
        self.pathlen = 10
        self.board_size = self.pathlen + 2
        goals = [Goal('NSEW', 'gold'), Goal('SW', 'stone'), Goal('NW', 'stone')]
        random.shuffle(goals)
        # the path has a single cell perimeter so that methods like reveal_goal_if_found
        # and set_path will work (since they test for adjacent cells by index).
        self.path = Grid(self.pathlen, self.pathlen)
        self.goal = [(2,8),(4,8),(6,8)]
        self.path[4][1] = self.ladder = Ladder()
        self.path[2][8] = goals[0]
        self.path[4][8] = goals[1]
        self.path[6][8] = goals[2]
        self.discovered_goal = None # the parent (Player object) of the agent that discovered the goal
        
    def gold_found(self):
        """
        Returns True if the gold has been revealed on the board.
        """
        for row in self.path:
            for item in row:
                if item and item.state == 'gold' and item.reveal:
                    return True
        return False

    def reveal_goal_if_found(self, item, row_idx, col_idx, dir_in, dir_out, agent):
        """
        Reveal any goal card if 1) the just placed path card is next to one, and
        2) there is a connection between the two cards. If a goal card is revealed that
        contains gold, then store the player's parent object for scoring purposes.
        """
        if (row_idx >= 1 and row_idx <= self.pathlen and col_idx >=1 and col_idx <= self.pathlen and
            isinstance(self.path[row_idx][col_idx], Goal) and
            self.path[row_idx][col_idx].exit.find(dir_in) >= 0 and
            item.enter.find(dir_out) >= 0 and
            dfs(FindLadderProblem(self.path[row_idx][col_idx], row_idx, col_idx, self))):
            path = self.path[row_idx][col_idx]
            path.reveal = True
            if path.state == 'gold':
                self.discovered_goal = agent.parent

    def set_path(self, agent, row, col, item):
        """
        Place an item at row, col and reveal any adjacent goal cards.
        """
        if row < 0 or row >= self.pathlen or col < 0 or col >= self.pathlen:
            return False

        self.path[row][col] = item
        for r, c, dir_in, dir_out in [(row-1, col, 'S', 'N'), (row+1, col, 'N', 'S'),
            (row, col-1, 'E', 'W'), (row, col+1, 'W', 'E')]:
            self.reveal_goal_if_found(item, r, c, dir_in, dir_out, agent)
        return True
